/**
 Übung 1 - Harmonische Schwingungen
========================================
 Harmonische Schwingung = Normalprojektion einer gleichförmigen Kreisbewegung
 auf die x- oder y-Achse
 
 Projektion auf die y-Achse
 	y(t) = r * sin (w * t)
 	w = 2 * pi / T = 2 * pi * f (Kreisfrequenz)
 	
 	r = maximale Auslenkung der Schwingung (Amplitutde)
 	T = Umlaufzeit der Kreisbewegung => ergibt die Periode der Schwingung
 
 **/

package saite;

//  -------------   JOGL SampleProgram  (Schwingende Saite) ------------

import gui.IUebung;

import javax.media.opengl.*;

import com.sun.opengl.util.FPSAnimator;
import com.sun.opengl.util.GLUT;

import java.awt.*;
import java.awt.event.*;

public class U1_SchwingendeSaite implements WindowListener, GLEventListener, KeyListener, IUebung {
	
	public enum MODE {GS,O1,O2,LINEARKOM};
	
	// OpenGL-Canvas
	private GLCanvas canvas;
	private double xmin = -0.1;
	private double xmax = Math.PI;
	private double ymin = -3;
	private double ymax = 3;
	private double mx = 0;
	private double my = 0;

	// Winkelgeschwindigkeit
	double omega = 1;
	double t, dt = 0.01;
	//je mehr Punkte desto besser die Aufloesung der Linie
	final int nPkte = 10000;

	
	private int menuselection = 4;
	
	//Wieviele Oberschwingunen zusammen dargestellt werden
	//Zeichnet beim start alle 3 Oberschwingungen zusammen
	private int scount = 3;
	
	//Koeffizienten fuer die Schwingungen
	private double[] coefficients = {0.5, 0.2, 0.1};
	
	private FPSAnimator anim;
	private GLUT glut;

	private int font = GLUT.BITMAP_HELVETICA_18;
	
	// ------------------ Methoden --------------------

	@Override
	public void start() {
		if (!anim.isAnimating()){
			anim.start();			
		}
	}

	@Override
	public void stop() {
		if (anim.isAnimating()){
			anim.stop();
		}		
	}

	public void modeSwtich(MODE mode) {
		switch(mode){
		case GS: menuselection = 1; break;
		case O1: menuselection = 2; break;
		case O2: menuselection = 3; break;
		case LINEARKOM: menuselection = 4; break;
		}
		
	}
	
	public MODE GetMode(){
		return MODE.values()[menuselection-1];
	}
	
	public double getOmega(){
		return omega;
	}
	
	public double getT(){
		return t;
	}

	// Koordinatenachsen zeichnen
	void zeichneAchsen(GL gl) {
		gl.glBegin(GL.GL_LINES);
		// x-Achse
		gl.glVertex2d(0, 0);
		gl.glVertex2d(Math.PI, 0);
		// y-Achse
		gl.glVertex2d(0, -3);
		gl.glVertex2d(0, 3);
		gl.glEnd();
	}
	
	// Zeichne ein simples Menue
	void zeichneMenu(GL gl){
		gl.glRasterPos2d(0.1, -0.8);
		glut.glutBitmapString(font, "Omega "+omega);
		gl.glRasterPos2d(0.1, -1.0);
		glut.glutBitmapString(font, "T "+ new Double(t).longValue());
		gl.glRasterPos2d(0.1, 1.6);
/*		switch (menuselection) {
		case 4:
			glut.glutBitmapString(font, "Linear Kombination der ersten 3 Oberschwingungen");
			break;
		case 3:
		case 2:
			glut.glutBitmapString(font, menuselection-1 +" Oberschwingung");
			break;
		case 1:
			glut.glutBitmapString(font, "Grundschwingung");
		default:
			break;
		}*/
	}

	void zeichneSaite_old(GL gl, double x, double y){
		// x-Schrittweite
		double xdt = Math.PI / nPkte;
//		for (int s = 1; s < 5; s=s*2){
			gl.glBegin(GL.GL_LINE_STRIP);
			for (int i = 0; i < nPkte; i++){
				//Punkte an den Enden der Saite haben Amplitude 0
				double amplitude_1 = 0;
				double amplitude_2 = 0;
				double amplitude_3 = 0;
				//Berechnung der einzelnen Amplituden
				if (!(i == 0 || i == nPkte-1)){
					amplitude_1 = Math.sin(1  * i * xdt);
					amplitude_2 = Math.sin(2  * i * xdt);
					amplitude_3 = Math.sin(3  * i * xdt);
				}
				double y1 = amplitude_1 * Math.cos(omega * t);
				double y2 = amplitude_2 * Math.cos(2 * omega * t);
				double y3 = amplitude_3 * Math.cos(3 * omega * t);
				//Linearkombination der einzelenen Schwingungen
				double linearkombi = 0.5 * y1 + 0.2 * y2 + 0.1 * y3;
				gl.glVertex2d(i * xdt, linearkombi);
			}
			gl.glEnd();
//		}
	}
	
	void zeichneSaite(GL gl, double x, double y){
		// x-Schrittweite
		double xdt = Math.PI / nPkte;
			gl.glBegin(GL.GL_LINE_STRIP);
			for (int i = 0; i < nPkte; i++){
				//Punkte an den Enden der Saite haben Amplitude 0
				double linearkombi = 0;
				//Berechnung der einzelnen Amplituden
				if (!(i == 0 || i == nPkte-1)){
					//Linearkombination der einzelenen Schwingungen
					for (int j=0 ; j < this.scount; ++j){
						linearkombi+=  coefficients[j] * Math.sin((j+1)  * i * xdt) * Math.cos((j+1) * omega * t);
					}
				}
				gl.glVertex2d(i * xdt, linearkombi);
			}
			gl.glEnd();
	}
	
	/*
	 * Zeichnet abhänging vom Parameter j die Linerakombination von:
	 * j = 1: Grundschwingung
	 * j = 2: Erste Oberschwingung
	 * j = 3: Zweite Oberschwingung
	 */
	void zeichneEinzelneSchwingung(GL gl, double x, double y, int j){
		// x-Schrittweite
		double xdt = Math.PI / nPkte;
			gl.glBegin(GL.GL_LINE_STRIP);
			for (int i = 0; i < nPkte; i++){
				//Punkte an den Enden der Saite haben Amplitude 0
				double linearkombi = 0;
				//Berechnung der einzelnen Amplituden
				if (!(i == 0 || i == nPkte-1)){
					linearkombi+=  coefficients[j-1] * Math.sin(j  * i * xdt) * Math.cos(j * omega * t);
				}
				//Linearkombination der einzelenen Schwingungen
				gl.glVertex2d(i * xdt, linearkombi);
			}
			gl.glEnd();
	}
	
	
	// Konstruktor
	public U1_SchwingendeSaite() {
		Frame f = new Frame("Schwingende Saite");
		f.setSize(800, 600);
		f.addWindowListener(this);
		// OpenGL-Window
		canvas = new GLCanvas();
		canvas.addKeyListener(this);
		canvas.addGLEventListener(this);
		f.add(canvas);
		f.setVisible(true);
		canvas.requestFocus();
		glut = new GLUT();
	}
	
    public U1_SchwingendeSaite(GLJPanel jp){
        jp.addKeyListener(this);
        jp.addGLEventListener(this);
        jp.setVisible(true);
        jp.requestFocus();
        glut = new GLUT();
    }

	// main-Methode der Applikation
	public static void main(String[] args) {
		new U1_SchwingendeSaite();
	}

	// --------- OpenGL-Events -----------------------

	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();// OpenGL-Objekt
		//Renderer fuer Text
		gl.glClearColor(0.68f, 0.84f, 0.9f, 1.0f); // erasing color
		anim = new FPSAnimator(drawable, 200, true);
		anim.start();
	}

	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT); // Bild loeschen
		gl.glColor3d(0, 0, 0); // Zeichenfarbe
		zeichneAchsen(gl);
		gl.glColor3d(1, 0.2, 0.1);
		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
//		zeichneSaite_old(gl, this.mx, this.my);
//		zeichneSaite(gl, this.mx, this.my);
		switch (menuselection) {
		case 4:
			zeichneSaite(gl, this.mx, this.my);
			break;
		case 3:
		case 2:
		case 1:
			zeichneEinzelneSchwingung(gl, this.mx, this.my, menuselection);
			break;
		default:
			break;
		}
//		zeichneMenu(gl);
		t += dt;
	}

	// Window resized
	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		GL gl = drawable.getGL();
		// damit keine Verzerrungen enstehen
		ymax = xmax * height / width;
		ymin = -ymax;
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrtho(xmin, xmax, ymin, ymax, -1, 1);
		gl.glViewport(0, 0, width, height);
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
			boolean deviceChanged) {
	}

	// --------- Window-Events --------------------

	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}

	public void windowActivated(WindowEvent e) {
	}

	public void windowClosed(WindowEvent e) {
	}

	public void windowDeactivated(WindowEvent e) {
	}

	public void windowDeiconified(WindowEvent e) {
	}

	public void windowIconified(WindowEvent e) {
	}

	public void windowOpened(WindowEvent e) {
	}



	@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_S:
				if (anim.isAnimating()) {
					anim.stop();
				} else {
					anim.start();
				}
			break;
		case KeyEvent.VK_UP:
				omega*=2;
			break;
		case KeyEvent.VK_DOWN:
				omega/=2;
			break;
		case KeyEvent.VK_LEFT:
				dt-=0.01;
			break;
		case KeyEvent.VK_RIGHT:
				dt+=0.01;
			break;
		case KeyEvent.VK_1:
			menuselection = 1;
			break;
		case KeyEvent.VK_2:
			menuselection = 2;
			break;
		case KeyEvent.VK_3:
			menuselection = 3;
			break;
		case KeyEvent.VK_4:
			menuselection = 4;
			break;
		default:
			break;
		}
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyTyped(KeyEvent e) {

	}

}